package com.moelholm.spring43.customannotations;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import javax.validation.constraints.NotNull;

/**
 * 定义一个注解处理器
 * 使用@Component将注解@BusinessService的处理器加入spring组件扫描器中
 * 使用@before定义注解处理器，即在使用这个注解的的前处理部分，加入处理
 *
 */
@Aspect
@Component//自动加载如spring
public class NotNullParameterAspect {

  //这是一个AOP里面的标签，表示调用BusinessService时，拦截，做前处理-->即非空检查
  @Before("@within(com.moelholm.spring43.customannotations.BusinessService)")
  public void before(JoinPoint caller) {

    Method method = getCurrentMethod(caller);
    //获取切点参数集合
    Object[] parameters = caller.getArgs();
    //获取方法参数的注解
    Annotation[][] parameterAnnotations = method.getParameterAnnotations();

    for (int i = 0; i < parameters.length; i++) {
      Object parameterValue = parameters[i];
      Annotation[] annotationsOnParameter = parameterAnnotations[i];

      //如果参数是空值，而且定义了@NotNull注解，那么抛出异常
      if (parameterValue == null && hasNotNullAnnotation(annotationsOnParameter)) {
        String msgTemplate = String.format("Parameter at index %s must not be null", i);
        throw new IllegalArgumentException(msgTemplate);
      }
    }

  }

  /**
   * 是否有一个注解是类型是@NotNull类型
   * @param annotations 注解类型 可变长列表
   * @return true/false
   */
  private boolean hasNotNullAnnotation(Annotation... annotations) {
    return Arrays.asList(
            annotations).stream().anyMatch(a -> a.annotationType() == NotNull.class);
  }

  private Method getCurrentMethod(JoinPoint joinPoint) {
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    return signature.getMethod();
  }
}